package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.WmNewsMessageConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 陈辉
 * @data 2023 10:28
 */
@Service
@Transactional
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    @Autowired
    private WmAutoScanService wmAutoScanService;
    @Autowired
    private WmTaskService wmTaskService;
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    /**
     * 文章上下架
     *
     * @param wmNewsDto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto wmNewsDto) {

        //0. 校验参数
        if (wmNewsDto == null || wmNewsDto.getId() == null || wmNewsDto.getEnable() == null)
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);

        //1. 根据id查询文章
        WmNews wmNews = getById(wmNewsDto.getId());

        //2. 校验文章是否存在：不存在，则直接返回结果，流程结束
        if (wmNews == null)
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);

        //3. 校验文章状态是否是：已发布，不是则直接返回结果，流程结束
        if (!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())){
            throw new CustomException(AppHttpCodeEnum.NO_OPERATOR_AUTH);
        }

        //4. 修改wm_news表的enable状态
        wmNews.setEnable(wmNewsDto.getEnable());
        updateById(wmNews);

        //5. 发送消息到kafka，通知article端进行文章配置的上下架操作
        Map<String,Object> map = new HashMap<>();
        map.put("articleId",wmNews.getArticleId());
        map.put("enable",wmNews.getEnable());
        kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(map));
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 内容列表 -- 多条件分页查询
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findByPage(WmNewsPageReqDto dto) {
        //1. 参数校验
        if (dto == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();

        //2. 构建分页对象
        Page ipage = new Page(dto.getPage(), dto.getSize());

        //3. 构建条件对象
        LambdaQueryWrapper<WmNews> wrapper = Wrappers.<WmNews>lambdaQuery().eq(dto.getStatus() != null, WmNews::getStatus, dto.getStatus())
                .between(dto.getBeginPubDate() != null && dto.getEndPubDate() != null, WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate())
                .eq(dto.getChannelId() != null, WmNews::getChannelId, dto.getChannelId())
                .like(StringUtils.isNotBlank(dto.getKeyword()), WmNews::getTitle, dto.getKeyword());

        wrapper.orderByDesc(WmNews::getPublishTime);

        //4. 分页条件查询
        page(ipage, wrapper);

        //5. 封装结果返回
        PageResponseResult result = new PageResponseResult((int) ipage.getCurrent(), (int) ipage.getSize(), (int) ipage.getTotal());
        result.setCode(AppHttpCodeEnum.SUCCESS.getCode());
        result.setData(ipage.getRecords());

        return result;
    }

    /**
     * 发布文章
     *
     * @param wmNewsDto
     * @return
     */
    @Override
    public ResponseResult submit(WmNewsDto wmNewsDto) {
        //参数校验
        if (wmNewsDto == null || StringUtils.isBlank(wmNewsDto.getContent())) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //数据处理: 将dto数据copy到entity中
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto, wmNews);

        //需要对封面图片进行单独处理： 将dto中的List集合转为entity中String
        if (wmNewsDto.getImages() != null && wmNewsDto.getImages().size() > 0) {
            List<String> imagesList = wmNewsDto.getImages();
            String images = String.join(",", imagesList);
            wmNews.setImages(images);
        }

        //处理封面类型为自动的情况：先将实体类的type置为null
        if (wmNewsDto.getType() == -1) {
            wmNews.setType(null);
        }

        //1. 新增或修改文章信息   -- wm_news表
        saveOrUpdateArticle(wmNews);

        //2. 判断是否是存入草稿：是的话，流程结束。  -- status：0?
        if (wmNews.getStatus() == 0) return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

        //3.1 提取出文章内容中的所有图片
        List<String> contentImageList = handleContentImg(wmNewsDto.getContent());

        //3. 不是草稿： 保存文章和内容图片的关联关系   -- wm_news_material表 type: 0
        saveRelationByContentImgWithNews(contentImageList, wmNews);

        //4. 不是草稿： 保存文章和封面图片的关联关系   -- wm_news_material表 type: 1
        saveRelationByCoverImgWithNews(contentImageList, wmNews, wmNewsDto);


        //5. 开启文章审核流程
        //wmAutoScanService.autoScan(wmNews.getId());

        //异步调用添加任务功能，将文章id携带到任务中
        wmTaskService.addTask(wmNews);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 保存封面图片和文章的关联关系
     *
     * @param contentImageList 内容图片集合
     * @param wmNews           文章信息
     * @param dto              前端传递过来的文章信息
     *  核心功能：
     *  1. 当type == -1时：实现自动模式的匹配
     *  a. 当内容图片数量 >= 3, 将type设置为 3，表示多图文章
     *  b. 3 > 当内容图片数量 >= 1, 将type设置为 1，表示单图文章
     *  c. 当内容图片数量 < 1, 将type设置为 0，表示无图文章
     *  2.  保存封面图片和文章的关联关系
     */
    private void saveRelationByCoverImgWithNews(List<String> contentImageList, WmNews wmNews, WmNewsDto dto) {
        //拿到前端传递的封面图片数据
        List<String> images = dto.getImages();
        //1. 当type == -1时：实现自动模式的匹配
        if (dto.getType() == -1) {
            if (contentImageList != null && contentImageList.size() >= 3){
                wmNews.setType((short)3);       //多图
                //将内容中的前三张图片截取出来作为封面图片
                images = contentImageList.stream().limit(3).collect(Collectors.toList());
            }else if (contentImageList != null && contentImageList.size() >= 1){
                wmNews.setType((short)1);       //单图
                //将内容中的前一张图片截取出来作为封面图片
                images = contentImageList.stream().limit(1).collect(Collectors.toList());
            }else{
                wmNews.setType((short)0);       //无图
            }

            if (images!=null && images.size() >0){
                wmNews.setImages(String.join(",",images));
            }

            //修改wm_news的信息
            updateById(wmNews);
        }

        //2. 保存封面图片和文章的关联关系
        saveRelationByImgWithNews(images,wmNews,1);
    }

    //保存内容图片和文章的关联关系
    private void saveRelationByContentImgWithNews(List<String> contentImageList, WmNews wmNews) {
        saveRelationByImgWithNews(contentImageList, wmNews, 0);
    }

    /**
     * 保存图片和文章关联关系
     *
     * @param contentImageList 图片url集合
     * @param wmNews           文章实体对象
     * @param type             0：内容引用   1：封面引用
     */
    private void saveRelationByImgWithNews(List<String> contentImageList, WmNews wmNews, int type) {
        if (contentImageList != null && contentImageList.size() > 0) {
            //1. 根据图片url查询对应的material_id
            //先对图片url去重处理
            contentImageList = contentImageList.stream().distinct().collect(Collectors.toList());
            List<Integer> materialIds = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery()
                            .in(WmMaterial::getUrl, contentImageList))
                    .stream().map(WmMaterial::getId).collect(Collectors.toList());

            //2. 批量保存素材与文章的关联关系
            wmNewsMaterialMapper.saveRelations(materialIds, wmNews.getId(), type);
        }
    }

    //提取文章内容中的图片
    private List<String> handleContentImg(String content) {
        JSONArray jsonArray = JSONArray.parseArray(content);

        List<String> resList = new ArrayList<>();

        //遍历集合，提取出所有图片
        for (Object obj : jsonArray) {
            JSONObject jsonObject = JSONObject.parseObject(obj.toString());
            if ("image".equals(jsonObject.getString("type"))) {
                String imgUrl = jsonObject.getString("value");
                resList.add(imgUrl);
            }
        }

        return resList;
    }

    //新增或修改文章方法
    private void saveOrUpdateArticle(WmNews wmNews) {
        //根据id来判断是新增还是修改
        if (wmNews.getId() != null) {
            //修改
            //先清空文章和素材的关联关系
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, wmNews.getId()));

            //再去修改文章信息
            wmNews.setSubmitedTime(new Date());
            updateById(wmNews);
        } else {
            //新增
            wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
            wmNews.setCreatedTime(new Date());
            wmNews.setSubmitedTime(new Date());
            save(wmNews);
        }
    }
}
